using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Drawing;

namespace AlgorithmsVisualized.Controls
{
    public class GraphPictureBox : PictureBox
    {
        #region DataMembers
        private List<ShapeBase> _shapes = new List<ShapeBase>();
        //private List<Type> _selectableTypes;
        private ShapeBase _selectedShape = null;
        #endregion
        #region Events
        public event EdgeHandler NewEdgeRequest;
        public event ShapeHandler SelectionChanged;
        #endregion
        #region Properties
        public ShapeBase SelectedShape
        {
            get { return _selectedShape; }
            set { _selectedShape = value; }
        }
        public ReadOnlyCollection<ShapeBase> Shapes
        {
            get { return _shapes.AsReadOnly(); }
        }
        #endregion
        #region Event Handlers
        protected override void OnMouseUp(MouseEventArgs e)
        {
            //MessageBox.Show("PictureBox is clicked");
            if (SelectedShape != null)
                SelectedShape.Deselect();
            List<ShapeBase> NewShapes = new List<ShapeBase>();
            foreach (ShapeBase shape in _shapes)
            {
                if (shape.IsInside(e.Location))
                {
                    shape.OnMouseUp(e);
                    if (ModifierKeys == Keys.Shift)
                    {
                        EdgeArgs args = new EdgeArgs((Node)SelectedShape, (Node)shape);
                        OnNewEdgeRequest(ref args);
                        Edge edge = Edge.CreateDefault((Node)SelectedShape, (Node)shape);
                        edge.IsDirected = args.IsDirected;
                        edge.Weight = args.Weight;
                        NewShapes.Add(edge);
                        SubscribeShapeEvents(edge);
                        Invalidate();
                    }
                    if (SelectedShape != null && shape != SelectedShape)
                        SelectedShape.Deselect();
                    SelectedShape = shape;
                    shape.Select();
                    OnSelectionChanged(new ShapeArgs(shape));
                }
            }
            _shapes.AddRange(NewShapes);
            /*foreach (Control c in Controls)
            {
                if (SelectableTypes.Contains(c.GetType()) && (c as ShapeBase).IsInside(new Point(e.X, e.Y)))
                {
                    SelectedShape = c;
                    c.Select();
                    break;
                }
            }*/

            base.OnMouseUp(e);
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (SelectedShape != null)
                SelectedShape.OnMouseMove(e);
            /*
            foreach (ShapeBase shape in _shapes)
            {
                if (shape.IsInside(e.Location))
                    shape.OnMouseMove(e);
            }
             */
            base.OnMouseMove(e);
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            foreach (ShapeBase shape in _shapes)
            {
                if (shape.IsInside(e.Location))
                {
                    if (ModifierKeys == Keys.Shift)
                        shape.OnKeyDown(new KeyEventArgs(Keys.Shift));
                    shape.OnMouseDown(e);
                    shape.OnKeyUp(new KeyEventArgs(Keys.Shift));
                }
            }
            base.OnMouseDown(e);
        }
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            
            if (e.Button == MouseButtons.Left)
            {
                Node n = Node.CreateDefault(new Point(e.X, e.Y));
                SubscribeShapeEvents(n);
                if (SelectedShape != null)
                    SelectedShape.Deselect();
                n.Select();
                OnSelectionChanged(new ShapeArgs(n));
                SelectedShape = n;
                _shapes.Add(n);
                Invalidate();
            }
            base.OnMouseDoubleClick(e);
            this.Focus();
        }

        protected virtual void OnNewEdgeRequest(ref EdgeArgs args)
        {
            if (NewEdgeRequest != null)
                NewEdgeRequest(this, ref args);
        }
        protected virtual void OnSelectionChanged(ShapeArgs args)
        {
            if (SelectionChanged != null)
                SelectionChanged(this, args);
        }
        protected override void OnPaint(PaintEventArgs pe)
        {
            Bitmap bit = new Bitmap(Width, Height);
            Graphics g = Graphics.FromImage(bit);
            PaintEventArgs ev = new PaintEventArgs(g, pe.ClipRectangle);
            foreach (ShapeBase shape in _shapes)
                shape.OnPaint(ev);
            Graphics orig = pe.Graphics;
            orig.Clear(BackColor);
            orig.DrawImageUnscaled(bit, 0, 0);
        }
        #endregion
        private void SubscribeShapeEvents(ShapeBase shape)
        {
            shape.RedrawRequest += shape_RedrawRequest;
        }
        void shape_RedrawRequest(object sender, EventArgs e)
        {
            Invalidate();
        }
    }
    public delegate void EdgeHandler(object sender, ref EdgeArgs args);
    public delegate void ShapeHandler(object sender, ShapeArgs args);
    public class ShapeArgs : EventArgs
    {
        private ShapeBase _shape;

        public ShapeBase Shape
        {
            get { return _shape; }
        }
        public ShapeArgs(ShapeBase shape)
        {
            _shape = shape;
        }
    }
    public class EdgeArgs : EventArgs
    {
        private Node _from;
        private Node _to;
        private bool _isDirected = false;
        private double _weight = 0.0;

        public double Weight
        {
            get { return _weight; }
            set { _weight = value; }
        }
        public bool IsDirected
        {
            get { return _isDirected; }
            set { _isDirected = value; }
        }
        public Node To
        {
            get { return _to; }
        }
        public Node From
        {
            get { return _from; }
        }
        public EdgeArgs(Node from, Node to)
        {
            _from = from;
            _to = to;
        }
    }
}
